﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
using M3G;

/**
 * k-DOPの主軸の定義は仕様書より
 * 0: (1, 0, 0) = x軸
 * 1: (0, 0, 1) = y軸
 * 2: (0, 1, 0) = z軸
 * 3: 以下略。
 * 従ってMin,Maxの値はx軸方向, z軸方向, y軸方向, (以下略)の順番で返ってくることに注意。
 * */

namespace MyTestProject {
    public class TestCollisionShape {
        [Fact]
        public void TestConstructor () {
            var cshp = new CollisionShape ();
            Assert.Equal (0, cshp.Orientations);
            Assert.Equal (null, cshp.Envelope);
        }

        /**
         * 最も簡単なコリジョン形状作成のテスト
         * */
        [Fact]
        public void TestConstruct6DOP1 () {
            var cshp = new CollisionShape ();
            var orientations = 1 << 2 | 1 << 1 | 1 << 0;
            var vertices = new float[] { 1, 1, 1,
                                         1, 1,-1,
                                         1, -1, 1,
                                        -1, 1, 1,
                                         1,-1,-1,
                                        -1, 1,-1,
                                        -1,-1, 1,
                                        -1,-1,-1};
            cshp.ConstructKDOP (orientations, vertices);

            var expectedMin = new float[] { -1,-1,-1 };
            var expectedMax = new float[] { 1, 1, 1 };

            Assert.Equal (orientations, cshp.Orientations);
            Assert.Equal (expectedMin, cshp.Min.Take(3));
            Assert.Equal (expectedMax, cshp.Max.Take(3));
        }

        /**
         * 物体をx方向に+1だけ移動したコリジョン形状作成のテスト
         * (非対称なので軸の±やmin,max処理が間違えているとこれでわかる)
         * */
        [Fact]
        public void TestConstruct6DOP2 () {
            var cshp = new CollisionShape ();
            var orientations = 1 << 2 | 1 << 1 | 1 << 0;
            var vertices = new float[] { 1,  0,  0,
                                         2,  0,  0,
                                         1,  1,  0,
                                         2,  1,  0,
                                         1,  0,  1,
                                         2,  0,  1,
                                         1,  1,  1,
                                         2,  1,  1};
            cshp.ConstructKDOP (orientations, vertices);

            var expectedMin = new float[] { 1, 0, 0 };
            var expectedMax = new float[] { 2, 1, 1 };

            Assert.Equal (orientations, cshp.Orientations);
            Assert.Equal (expectedMin, cshp.Min.Take (3));
            Assert.Equal (expectedMax, cshp.Max.Take (3));
        }

        /**
         * コーナーカットしたコリジョン形状作成のテスト
         * (他は全て6DOPなのでこのテストが必要)
         * */
        [Fact]
        public void TestConstruct8DOP () {
            var cshp = new CollisionShape ();
            var orientations = 1<<12 | 1 << 11 | 1 << 10 | 1 << 9;
            var vertices = new float[] { 1,  0,  0,
                                         2,  0,  0,
                                         1,  1,  0,
                                         2,  1,  0,
                                         1,  0,  1,
                                         2,  0,  1,
                                         1,  1,  1,
                                         2,  1,  1};
            cshp.ConstructKDOP (orientations, vertices);

            var expectedMin = new float[13] { 0, 0, 0, 0 , 0, 0, 0, 0, 0, 0.5773503f, 0          , 0          , -0.5773503f};
            var expectedMax = new float[13] { 0, 0, 0, 0 , 0, 0, 0, 0, 0, 2.3094010f, 1.73205075f, 1.73205078f, 1.15470052f};

            Assert.Equal (orientations, cshp.Orientations);
            Assert.Equal (expectedMin, cshp.Min, new FloatComparere(0.00001f));
            Assert.Equal (expectedMax, cshp.Max, new FloatComparere (0.00001f));
        }

        /**
         * エッジカットしたコリジョン形状作成のテスト
         * (他は全て6DOPなのでこのテストが必要)
         * */
        [Fact]
        public void TestConstruct12DOP () {
            var cshp = new CollisionShape ();
            var orientations = 1<<8 | 1<<7 | 1 << 6 | 1 << 5 | 1 << 4 | 1 << 3;
            var vertices = new float[] { 1,  0,  0,
                                         2,  0,  0,
                                         1,  1,  0,
                                         2,  1,  0,
                                         1,  0,  1,
                                         2,  0,  1,
                                         1,  1,  1,
                                         2,  1,  1};
            cshp.ConstructKDOP (orientations, vertices);

            var expectedMin = new float[13] { 0, 0, 0, 0.7071068f, 0, 0.7071068f, 0, 0, -0.7071068f, 0, 0, 0, 0 };
            var expectedMax = new float[13] { 0, 0, 0, 2.12132f, 1.414214f, 2.12132f, 1.414214f, 1.414214f, 0.7071068f, 0, 0, 0, 0 };

            Assert.Equal (orientations, cshp.Orientations);
            Assert.Equal (expectedMin, cshp.Min, new FloatComparere (0.00001f));
            Assert.Equal (expectedMax, cshp.Max, new FloatComparere (0.00001f));
        }


        /**
         * エンベロープの作成のテスト
         * */
        [Fact]
        public void TestGenerateEnvelop () {
            var cshp = new CollisionShape ();
            var orientations = 1 << 2 | 1 << 1 | 1 << 0;
            var vertices = new float[] { 1,  0,  0,
                                         2,  0,  0,
                                         1,  1,  0,
                                         2,  1,  0,
                                         1,  0,  1,
                                         2,  0,  1,
                                         1,  1,  1,
                                         2,  1,  1};
            cshp.ConstructKDOP (orientations, vertices);
            cshp.GenerateEnvelope ();

            // エンベロープの並びは順不同
            var expected = new float[] { 2, 1, 1,
                                         2, 0, 1,
                                         2, 1, 0,
                                         2, 0, 0,
                                         1, 1, 1,
                                         1, 0, 1,
                                         1, 1, 0,
                                         1, 0, 0};

            Assert.Equal (expected, cshp.Envelope);
        }

        /**
         * k-DOPの座標変換と更新のテスト
         * */
        [Fact]
        public void TestTransform () {
            var cshp = new CollisionShape ();
            var orientations = 1 << 2 | 1 << 1 | 1 << 0;
            var vertices = new float[] { 1,  0,  0,
                                         2,  0,  0,
                                         1,  1,  0,
                                         2,  1,  0,
                                         1,  0,  1,
                                         2,  0,  1,
                                         1,  1,  1,
                                         2,  1,  1};
            cshp.ConstructKDOP (orientations, vertices);
            cshp.GenerateEnvelope ();

            var expectedMin = new float[] { 1, 0, 0 };
            var expectedMax = new float[] { 2, 1, 1 };

            // オリジナルのk-DOP
            // x = [1, 2]
            // y = [0, 1]
            // z = [0, 1]
            Assert.Equal (orientations, cshp.Orientations);
            Assert.Equal (expectedMin, cshp.Min.Take(3));
            Assert.Equal (expectedMax, cshp.Max.Take (3));

            // z軸を中心に90度回転
            var tra = new Transform ();
            tra.PostRotate (90, 0, 0, 1);
            cshp.Transform (tra);
            expectedMin = new float[] { -1, 0, 1 };
            expectedMax = new float[] { 0, 1, 2 };

            // 回転後ののk-DOP
            // x = [-1, 0]
            // y = [1, 2]
            // z = [0, 1]
            Assert.Equal (orientations, cshp.Orientations);
            Assert.Equal (expectedMin, cshp.Min.Take (3), new FloatComparere(0.00001f));
            Assert.Equal (expectedMax, cshp.Max.Take (3), new FloatComparere (0.00001f));
        }

        /**
         * 長さが0の軸を含むコリジョン領域のテスト
         * */
        [Fact]
        public void TestThinCollisionShape () {
            var cshp = new CollisionShape ();
            var orientations = 1 << 2 | 1 << 1 | 1 << 0;
            var vertices = new float[] { 1,  0,  0,
                                         1,  1,  0,
                                         1,  0,  1,
                                         1,  1,  1};
            cshp.ConstructKDOP (orientations, vertices);
            cshp.GenerateEnvelope ();

            // エンベロープの並びは順不同
            var expected = new float[]  {1, 1, 1,
                                         1, 0, 1,
                                         1, 1, 0,
                                         1, 0, 0,
                                         1, 1, 1,
                                         1, 0, 1,
                                         1, 1, 0,
                                         1, 0, 0 };

            Assert.Equal (expected, cshp.Envelope);
        }
    }
}
